﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

using zijian666.Core;
using zijian666.DI.Features;

namespace zijian666.DI;
internal sealed class DynamicLauncher(string? name, Delegate? configure = null, Delegate? stop = null) : ILauncher
{
    private readonly string _name = name ?? nameof(DynamicLauncher);
    private readonly Delegate? _configure = configure;
    private readonly Delegate? _stop = stop;
    private Task? _configured;
    private Task? _stoped;

    public static Task CallDelegate(Delegate @delegate, IServiceProvider provider, object[]? args, CancellationToken cancellationToken)
    {
        try
        {
            args = provider.GetServices<IHost>().OfType<object>().Union(args ?? []).Append(cancellationToken).Distinct().ToArray();
            return new ServiceProviderProxy(provider, args).InvokeAsync(@delegate);
        }
        catch (Exception e)
        {
            throw new InvalidOperationException($"调用方法 {@delegate.Method} 失败:" + e.Message, e);
        }
    }

    public Task ConfigureAsync(IServiceProvider provider, object[]? args, CancellationToken cancellationToken)
    {
        if (_configure is null)
        {
            return Task.CompletedTask;
        }
        return OnceCaller.ExecuteAsync(ref _configured, async () =>
        {
            var logger = provider.GetService<ILogger<DynamicLauncher>>();
            using var _ = logger?.BeginScope("ConfigureAsync");
            logger?.LogInformation($"正在配置 {_name}");
            var filter = FeatureManager.Gets<IConfigureServicesFilter>().ToList();
            filter.ForEach(x => x.BeginConfigure(provider, typeof(DynamicLauncher), args ?? []));
            await CallDelegate(_configure, provider, args, cancellationToken);
            filter.ForEach(x => x.EndConfigure(provider, typeof(DynamicLauncher), args ?? []));
            logger?.LogInformation($"已配置 {_name}");
        });
    }

    public Task ConfigureServicesAsync(IServiceCollection services, object[]? args, CancellationToken cancellationToken)
        => Task.CompletedTask;

    public Task StopAsync(IServiceProvider provider, object[]? args, CancellationToken cancellationToken)
    {
        if (_stop is null)
        {
            return Task.CompletedTask;
        }

        return OnceCaller.ExecuteAsync(ref _stoped, async () =>
        {
            var logger = provider.GetService<ILogger<DynamicLauncher>>();
            using var _ = logger?.BeginScope("StopAsync");
            logger?.LogInformation($"正在停止 {_name}");
            await CallDelegate(_stop, provider, args, cancellationToken);
            logger?.LogInformation($"已停止 {_name}");
        });
    }
}
